package net.cyndeline.scalarlib.rldungeon.dgs.strategy.pointlessArea.help

import scalax.collection.immutable.Graph
import net.cyndeline.scalarlib.rldungeon.dgs.graph.{DGSFunctionality, Room}
import net.cyndeline.scalarlib.rldungeon.dgs.strategy.help.{CollapsedEdge, CollapsedNode}
import scalax.collection.GraphEdge.UnDiEdge

/**
 * Used for injection.
 */
trait BottleneckSetterI {
  def markBottlenecks[VType <: Room[VType], EType[X] <: UnDiEdge[X] with DGSFunctionality[X, EType]]
  (originalGraph: Graph[VType, EType],
   superGraph: Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner]): Graph[VType, EType]
}
/**
 * Marks which edges in a graph that are eligible for responders, on the condition that they aren't inside a cycle.
 * In other words, edges that must be traversed when visiting every room on the map.
 *
 * @constructor Create a new bottleneck setter.
 */
class BottleneckSetter extends BottleneckSetterI {

  /**
   * Marks edges as bottlenecks for in every edge of a graph where its collapsed equivalent has edges
   * (i.e for every edge that wasn't folded into a cycle).
   * @param originalGraph Graph to set responder capacity for.
   * @param superGraph Cycle-collapsed representation of the original graph.
   * @tparam VType Type of vertex in the original graph.
   * @tparam EType Type of edge in the original graph.
   * @return A copy of the original graph with every edge not collapsed in the super-graph having its responder
   *         capacity set to true.
   */
  def markBottlenecks[VType <: Room[VType], EType[X] <: UnDiEdge[X] with DGSFunctionality[X, EType]]
                                           (originalGraph: Graph[VType, EType],
                                            superGraph: Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner]): Graph[VType, EType] = {
    var resultingGraph = originalGraph
    val collapsedEdges = superGraph.edges.iterator

    while (collapsedEdges.hasNext) {
      val edge = collapsedEdges.next().toOuter
      val targets = CollapsedEdge.targetsOfEdge(edge)
      val from: VType = targets._1
      val to: VType = targets._2

      val oldEdge = originalGraph.edges.find(e => (e._1 == from && e._2 == to) || (e._2 == from && e._1 == to)).getOrElse {
        throw new IllegalArgumentException("The graph " + originalGraph + " did not contain an edge with the vertices " + from + " and " + to + " as found in the collapsed graph " + superGraph)
      }
      val newEdge = oldEdge.toOuter.setBottleneckStatus(true)
      resultingGraph -= oldEdge
      resultingGraph += newEdge
    }

    resultingGraph
  }
}
